Data Structures
Arrays
|
|
|
|
Arrays are homogeneous structures. |
|
An array is something that you can
stick a lot of different things into, but all of them must be the same type. |
|
An array is a group of things that are
together because they are the same type not necessarily the same value. |
|
|
Arrays
An example
|
|
|
Let us consider an array of social
security numbers. Let picture of the disk represents each of the numbers: |
|
<data type> <array name>[<size>]; |
|
int SSN[5]; |
|
SSN[1] = 345; |
|
|
Arrays
Declaration
|
|
|
An array is a collection of variables
of the same type. Individual array elements are identified by an integer
index. In C the index begins at zero and is always written inside square
brackets. |
|
|
|
int results[20]; |
|
int results_2d[20][5]; |
|
int results_3d[20][5][3]; |
Arrays
Initialisation
Declarations of arrays
(Cont.)
Pascal C/C++
|
|
|
var |
|
a: array[0..9] of integer; |
|
b: array[0..4, 0..9] of integer; |
|
int a[10]; |
|
float b[5][10]; |
|
|
|
Arrays in C always have subscripts
ranging from 0 to declared size – 1. |
Arrays
An Example
|
|
|
#include <iostream.h> |
|
int main() { |
|
int x, y, anarray[8][8];//declares
an array like a chessboard |
|
for(x=0; x<8; x++) { // sets the
element to |
|
for(y=0; y<8; y++) { // zero;
after the loop all |
|
anarray[x][y]=0; // elements
== 0 |
|
} } |
|
for(x=0; x<8;x++) { |
|
for(y=0; y<8; y++) {
cout<<"anarray["<<x<<"]["<<y<<"]="<<anarray[x][y]<<"
"; |
|
} } |
|
return 0; |
|
} |
Arrays and functions
An Example
|
|
|
int add_array(int array[], int
size) |
|
{ |
|
int i; |
|
int total = 0; |
|
|
|
for (i=0; i < size; i++) |
|
total += array[i]; |
|
|
|
return(total); |
|
} |
Arrays:
Things to remember
|
|
|
|
An array identifier is a constant
pointer. |
|
Arrays are stored by rows. |
|
Let AnArray be a 2-dimensional array of
type type. |
|
The type of AnArray is a pointer to a
row. |
|
The type of *AnArray is a pointer to type. |
|
In the expression AnArray+i, i is
scaled by the size of the row. |
|
In the expression *AnArray+i, i is
scaled by the size of type. |
|
*AnArray = AnArray[0] |
|
**AnArray = *(AnArray[0]) =
AnArray[0][0] |
|
AnArray[i] = *(AnArray+i) = *AnArray +
i* number of columns |
Arrays:
Things to remember
|
|
|
|
If AnArray is a single-dimensional
array of type type passed as parameter to a function, AnArray may be declared
as |
|
type AnArray[]; |
|
The size of AnArray may be specified,
but it is neglected. |
|
type * AnArray; |
|
If AnArray is 2-dimensional array of type
passed as parameter to a function, AnArray may be declared as |
|
type AnArray[][size2]; |
|
And the specification of the constant size2
must not be omitted. |
Pointers
|
|
|
|
Pointers are data types. |
|
They store memory addresses of
variables. |
|
|
Pointers:
Definition
|
|
|
The pointer is like any other variable
except that the value stored in it has a different interpretation: It is the address
of some memory location. |
|
Pointer is variable that holds a memory
address |
Pointer:
Interpretation
|
|
|
ThePointer is defined as a variable of
the type pointer to integer, or simply, pointer. Assume value 5FA62 is stored
in ThePointer. This value is interpreted as a memory address. |
|
If the contents of memory location 5FA62
is 658, then the value of ThePointer is 5FA62 and ThePointer points to the
value 658. |
|
|
|
|
|
|
|
Sometimes the address stored in a
pointer is not important, so the following abbreviation can be used: |
Pointer:
Dereferencing mechanism
|
|
|
Since the value stored in a pointer is
a memory address, the language has to provide a dereferencing mechanism: the
mechanism that can be used to access the value pointed to by the pointer. &px
has the value 654. |
|
& is called the address operator. |
Dereferencing pointers
and the address operator
|
|
|
|
Dereferencing refers to accessing the
value that a pointer points to. |
|
To do this “*” operator that interprets
single argument as an address and returns the contents of the address as the
value of the operation. Usage: |
|
*p |
|
EXAMPLE: |
|
int i = 25, *j; |
|
Here, j is a pointer to integer. Assume
i and j are allocated memory cells at addresses 5FA62 and 5FA70,
respectively. This means that the value of i is 25, &i is 5FA62 and the
value of &j is 5FA70. |
Pointer:
Accessing data
|
|
|
The initial value of a variable,
including a pointer variable, is undetermined. Let assume that the initial
value stored in p is 5FA62: |
Pointer:
Accessing data
|
|
|
The initial value of a variable,
including a pointer variable, is undetermined. Let assume that the initial
value stored in p is 5FA62: |
|
|
|
|
|
|
Pointer Assignment Ex.5300
Pointerst Test
yourself
|
|
|
#include <iostream.h> |
|
int main() |
|
{ |
|
int x; //A normal integer |
|
int *pointer; //A pointer to an
integer |
|
pointer=&x; //Read it, |
|
//"pointer
equals the address of x“ |
|
cin>>x; //Reads in x |
|
cout<<*pointer; //Note the
use of the * to // output the actual number stored in x |
|
return 0; |
|
} |
Pointers
Pascal C/C++
|
|
|
var |
|
p: ^integer; |
|
first: ^student; |
|
struct student *first; |
Pointers
(Cont.)
Pascal C/C++
|
|
|
type |
|
stuptr = ^node; |
|
var |
|
first: stuptr; |
|
typedef |
|
struct student *stuptr; |
|
stuptr first; |
|
|
The NULL pointer
|
|
|
|
The special value NULL can be assigned
to the pointer to indicate that its value is defined but that it does not
point to anything. |
|
The NULL is usually equal to 0. |
|
A convenient way to set the initial
value of a pointer to NULL is in the definition itself: |
|
|
|
int *pi = NULL, *pj = NULL; |
Dynamic memory allocation
|
|
|
|
In C++, the new and delete operators
provide build-in language support for dynamic memory allocation and
deallocation. |
|
This feature has several benefits: |
|
Reduces common programmer errors: it is
easy to forget to multiply the number of objects being allocated by sizeof
when using malloc. |
|
Enhances source code clarity:
generally, there is no need to: (1) declare operator new and delete, (2)
explicitly use casts, or (3) explicitly check the return value. |
|
Improves run-time efficiency: (1) users
can redefine operator new and delete globally and also define then on a
per-class basis and (2) calls can be inlined. |
new() operator
|
|
|
The keyword new is used to initialise
pointers with memory from free store (a section of memory available to all
programs). |
|
|
|
int *ptr = new int; |
|
|
|
It initialises ptr to point to a memory
address of size int (because variables have different sizes, number of bytes,
this is necessary). The memory that is pointed to becomes unavailable to
other programs. This means that the careful coder will free this memory at
the end of its usage. |
delete() operator
|
|
|
The delete operator frees up the memory
allocated through new. To do so, the syntax is as in the example.
delete
ptr;
After deleting a pointer, it can be a good idea to reset it to point to NULL. |
|
|
|
NULL is a standard compiler-defined
statement that sets the pointer to point to, literally, nothing. By doing
this, you minimise the potential for doing something foolish with the
pointer. |
new() and
delete()
Example
new() and
delete()
Some remarks
|
|
|
new returns a pointer to the allocated
memory |
|
The storage duration of the new object
is from the point of creation until the operator delete destroys it by
deallocating its memory, or until the end of the program. |
|
By default, an allocation failure (such
as insufficient or fragmented heap memory) results in the predefined
exception bad_alloc being thrown. |
|
You should use the delete operator to
remove all memory which has been allocated by the new operator. Failure to
free memory can result in memory leaks. |
|
The delete operator offers dynamic
storage deallocation, deallocating a memory block allocated by a previous
call to new. |
Example: allocate 2D
array, initialise and delete it.
|
|
|
#include <iostream.h> |
|
void display(long double **); |
|
void de_allocate(long double **); |
|
int NumRows = 3; |
|
int NumCols = 5; |
|
void display(long double **data) { |
|
for (int i = 0; i < m; i++) { |
|
for (int j = 0; j < n; j++) |
|
cout << data[i][j] << " "; |
|
cout << "\n" << endl; |
|
} |
|
} |
Example: allocate 2D
array, initialise and delete it.
|
|
|
void de_allocate(long double **data) { |
|
for (int i = 0; i < NumRows;
i++) |
|
delete[] data[i]; // STEP 1: delete the columns |
|
delete[] data; // STEP 2:
delete the rows |
|
} |
|
|
|
void initialise(long double ** data){ |
|
for (int i = 0; i < NumRows; i++) |
|
for (int j = 0; j < NumCols; j++) |
|
data[i][j] = i + j;
// arbitrary initialisation |
|
} |
Example: allocate 2D
array, initialise and delete it.
|
|
|
int main(void) { |
|
long double **data; |
|
data = new long double*[NumRows]; // STEP 1: SET UP THE ROWS. |
|
for (int j = 0; j < NumRows; j++) |
|
data[j] = new long double[NumCols]; |
|
// STEP 2: SET UP THE COLUMNS |
|
|
|
initialise(data); |
|
display(data); |
|
de_allocate(data); |
|
return 0; |
|
} |
Arrays and Pointers
|
|
|
One thing that arrays don't require
that other variables do, is a reference operator when you want to have a
pointer to the string. |
|
EXAMPLE: |
|
|
|
|
|
//As opposed to |
|
|
|
|
|
|
|
|
Arrays and Pointers
|
|
|
The array can be initialised using
pointers as follows. |
|
|
|
ArrayExample = new int[25]; |
|
which allows to access ArrayExample
just as if it were an array. Keep in mind that to use delete you must put []
between delete and ArrayExample to tell it to free all 25 bytes of memory
allocated.
delete [] ArrayExample; |
The heap
|
|
|
|
The new routine reserves a block of
memory of the requested size in a pool of free memory called the heap. |
|
Two separate data storage areas are
used by the program during run time: the stack and the heap. |
|
The lifetime of data stored on the
stack is relative to the lifetime of the subroutine that the data are defined
in. |
|
The lifetime of data stored in the heap
starts with the execution of the allocation procedure new and ends when the
deallocation procedure delete is called. |
heap
operations
Advanced
|
|
|
Works with vectors |
|
A heap is a binary tree in which every
node is larger than the values associated with either child. A heap and a
binary tree, for that matter, can be very efficiently stored in a vector, by
placing the children of node i in positions 2 * i + 1 and 2 * i + 2. |
|
Using this encoding, the largest value
in the heap is always located in the initial position, and can therefore be
very efficiently retrieved. |
|
|
heap
operations
Advanced
|
|
|
Works with vectors |
|
A heap is a binary tree in which every
node is larger than the values associated with either child. A heap and a
binary tree, for that matter, can be very efficiently stored in a vector, by
placing the children of node i in positions 2 * i + 1 and 2 * i + 2. |
|
Using this encoding, the largest value
in the heap is always located in the initial position, and can therefore be
very efficiently retrieved. |
|
|
heap
operations
Advanced
|
|
|
A heap is a particular organization of
elements in a range between two random access iterators [a, b). Its two key
properties are: |
|
|
|
1. *a is the largest element in the
range. |
|
2. *a may be removed by the pop_heap
algorithm, or a new element can be added by the push_heap |
|
algorithm, in O(logN) time. |
|
|
|
These properties make heaps useful as priority
queues. |
|
The heap algorithms use less than
(operator<) as the default comparison. In all of the algorithms, an
alternate comparison operator can be specified. |
Pointers:
Things to remember
|
|
|
Pointers are NOT integers. |
|
Always to test whether or not memory
allocation requests are successful. |
|
Only memory that has been allocated
using new (or the related routines) should be freed using delete operator. |
|
To pass a parameter by variable,
specify this parameter that is a pointer, use a pointer to pointer. |
|
Always cast the NULL pointer when it is
passed as an actual parameter. |
|
Do not declare pointer as a global
variable. |
Glossary
|
|
|
Allocation of memory The action of
acquiring a block of memory from the heap. |
|
Deallocation of memory The action of
returning a block of memory to the heap. |
|
Dereferencing of a pointer’s value The
value pointed to by a pointer. |
|
Heap A pool of free memory. Dynamic
memory requires allocate memory from this area. |
|
Pointer A variable whose value is
interpreted as a memory address. A pointer points to the value stored in the
location at that address. |
|
|
Structures
Declaration
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
Structures
Declaration: Example (Step 1)
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
Structures
Declaration: Example (Step 2)
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
Structures
Declaration: Example (Step 3)
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
Structures
Declaration: Example (Step 4)
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
Structures
Initialisation Test
yourself
|
|
|
A C structure variable may be
initialised when it is declared. |
Structures and
Arrays
Initialisation
|
|
|
A C structure variable may be
initialised when it is declared – e.g. |
Structures
Example 1: Declaration
Structures and
Pointers
Declaration
|
|
|
|
Declaration of pointers to structures: |
|
|
|
|
|
|
|
|
|
This defines John as a structure and pJohn
as a pointer to a structure. |
|
pJohn variable initialisation: |
|
pJohn can be made to point to John: |
|
pJohn = &John; |
|
new can be used to allocation memory
for pJohn: |
|
int *pJohn = new student; |
Structures and
Pointers
Access to data
|
|
|
|
The first attempt to access the fields
of the structure John through pJohn can be: |
|
(*pJohn).StudentID; |
|
|
|
An alternative way: |
|
pJohn->StudentID; |
Structures and Pointers
Example 2: Access to data
|
|
|
#include <iostream.h> |
|
struct example { int field1; int
field2; }; |
|
int main() |
|
{ example Structure; |
|
example *PointerToStructure; |
|
Structure.field1=12; |
|
PointerToStructure =&
Structure; //& is necessary when
dealing with structures and using pointers to them |
|
cout<<
PointerToStructure->field1; //The -> acts somewhat like the * when used
with pointers. It says, get whatever is at that memory address Not "get
what that memory address is" |
|
return 0; |
|
} |
Structures and
pointers
Example 3: Using two structures
Structures and
pointers
Example 3: Using two structures
Structures and
pointers
Example 3: Using two structures
Structures
Stack principle
|
|
|
NOTE: Each of the big blocks is a struct
(or class) that has a pointer to another one. Remember that the pointer only
stores the memory location of something, it is not that thing, so the arrow
goes to the next one. At the end, there is nothing for the pointer to point
to, so it does not point to anything, it should be set to "NULL" to
prevent it from accidentally pointing to a totally arbitrary and random
location in memory (which is very bad). |
Structures
Example 3: Stack
|
|
|
struct node { |
|
int value; |
|
node *next; |
|
}; |
|
int main() { |
|
node *root; This will be the
unchanging 1st node |
|
root=new node; Now root points to a
node struct |
|
root->next=NULL; //The node root
points to has its |
|
// next pointer set equal to NULL |
|
root->value=5; // By using the
-> operator, you |
|
// can modify the node |
|
return 0; //a struct (root in this
case) points to. |
|
} |
Stack
Example 4
|
|
|
int main() { |
|
//This won't
change, or we would lose the list in memory |
|
//This will
point to each node as it traverses the list |
|
//Sets
it to actually point to something |
|
//Otherwise
it would not work well |
|
|
|
//The
conductor points to the first node |
|
|
|
|
|
// Creates
a node at the end of the list |
|
//Points
to that node |
|
//Prevents
it from going any further |
|
|
|
} |
Structures
Traversal function
|
|
|
conductor=root; |
|
// Makes sure there is a place to
start |
|
if(conductor!=NULL) { |
|
while(conductor->next!=NULL) { |
|
cout<<conductor->x; |
|
conductor=conductor->next; |
|
} |
|
cout<<conductor->x; |
|
} |
Construct a
list
Example (Step 1)
Construct a list
Example (Step 2)
Construct a list
Example (Step 3)
Construct a list
Example (Step 4)
Structures and Functions
|
|
|
Functions may return pointers to
structures, and they may have parameters that are pointers to structures. |
|
A function may return a structure, and
structures may be passed as parameters. |
Structures and
Functions
Example Test yourself
|
|
|
TASK: The program discussed shows 2 functions: *new() and new1
that create a new object for the structure |
Structures
Pascal C/C++
|
|
|
var |
|
student: record |
|
id: integer; |
|
name: packed array[1..10] of
char; |
|
gpa: real |
|
end; |
|
struct |
|
{ int id; |
|
char name[11]; |
|
float gpa; |
|
} student; |
Structures
(Cont.)
Pascal C/C++
|
|
|
var |
|
borrower: record |
|
case boolean of |
|
false: (EBorr: emploee); |
|
true: (SBorr: student) |
|
end; |
|
union |
|
{
emploee EBorr; |
|
student SBorr; |
|
} borrower; |
Structures
(Cont.)
Pascal C/C++
|
|
|
var |
|
borrower: record |
|
case IsStudent of |
|
false: (EBorr: emploee); |
|
true: (SBorr: student) |
|
end; |
|
NO DIRECT EQUIVALENT IN C. THIS CAN BE
HANDLED BY CREATING A STRUCT WHICH CONTAINS THE TAG AND A UNION AS ITS
FIELDS. (SEE NEXT SLIDE) |
Structures
(Cont.)
Pascal C/C++
|
|
|
var |
|
borrower: record |
|
id: integer; |
|
name: packed array[1..10] of char; |
|
case boolean of |
|
false: (EBorr: emploee); |
|
true: (SBorr: student) |
|
end; |
|
struct |
|
{
int id; |
|
char name[11]; |
|
union |
|
{ emploee EBorr; |
|
student SBorr; |
|
} Borr; |
|
} borrower; |
Structures:
Initialisation
Pascal C/C++
|
|
|
NO STANDARD PASCAL EQUIVALENT (BUT MANY
DIALECTS ALLOW THIS) |
|
A C variable may be initialised when it
is declared – e.g. |
|
int i = 3; |
|
char c = ‘A’; |
|
float a[3] = {1.0, 2.0, 3.0}; |
|
struct |
|
{ char name[11]; |
|
float gpa; |
|
} student = {“Smith”, 4.0}; |
Structures
Common errors
Structures
Things to remember
|
|
|
Structures are stored in blocks of
consecutive memory locations with padding if memory alignment is required. |
|
Structure assignments and passing a
structure as a parameter involves copying the entire structure. |
|
C does not permit self-referential
structure definitions: |
Structures and
Pointers
Things to remember
|
|
|
Attempts to access the fields of the
structure John through PointerJohn that is defined as |
|
PointerJohn = &John;. |
|
‘.’ has higher precedence than *, so
the expression |
|
|
|
would be interpreted as: |
|
|
|
The following expression is get out of
the problem: |
|
|
|
As an alternative notation that is
equivalent to the preceding expression is: |